from nodeinfo import Info
import collections
import copy

def __init__(self, nodeDict):
    self.globalNode = nodeDict
#Done
def addnode(allnodes, nodeobj):
    allnodes.setdefault(nodeobj.getName(), nodeobj)

#Done
def getServerNode(nodedict):
    nodeObj = Info()
    for key in nodedict.iterkeys():
        nodeObj = nodedict.get(key)
        if (nodeObj.getParent() == ('server')):
            return nodeObj
                        
# Testing Done
# It finds the highest and lowest Delay/Bandwidth of the whole network
def findHiLoDB(delayDict, bandWidthDict):
    nDict = {}
    # Sorting the imcoming delayDict on ascending order as a list
    Delay = collections.namedtuple('Delay', 'val name')
    ascDelayNode = sorted([Delay(a, b) for (b, a) in delayDict.items()])
    # Sorting the incoming bandWidthDict on ascending order as a list
    BandWidth = collections.namedtuple('BandWidth', 'val name')
    ascBandWidthNode = sorted([BandWidth(a,b) for (b,a) in bandWidthDict.items()])
    # Adding the highest and lowest DelayBandwidth in nDict(Dictionary)
    nDict.setdefault('hiDelay', ascDelayNode[-1].val)
    nDict.setdefault('loDelay', ascDelayNode[0].val)
    nDict.setdefault('loBandwidth', ascBandWidthNode[0].val)
    nDict.setdefault('hiBandwidth', ascBandWidthNode[-1].val)
    return nDict

# Testing Done
# It finds those nodes which are directly getting the data from the server
def findServerAsParent(nodeDict):
    nodelist = []
    nodeObj = Info()
    for k in nodeDict.iterkeys():
        nodeObj = nodeDict.get(k)
        if (nodeObj.getParent() == 'server'):
            nodelist.append(nodeObj.getName())
    return nodelist            


# Testing Done      
# It returns nodeDict(dictionary) removing the nodes from nodeDict which are in nodeList
def removeNodefromDict(nodeList, nodeDict):
    for i in nodeList:
        nodeDict.pop(i)
    return nodeDict


# Testing done    
# It returns list of all the child nodes of a node from nodeDict
def getNextChildNodes(nodeList, nodeDict):
    childNodes = []
    childLi = []
    for child in nodeList:
        childLi = nodeDict.get(child).getChild()
        for childName in childLi:
            childNodes.append(childName)
    return childNodes        


# Testing done
# It adds all the child node to delayDict(Dictionary) along with its
# actual delay as the summation of (self delay and parent delay)
def appendToDelayDict(delayDict, nodeList, nodeDict):
    if not delayDict:
        for i in nodeList:
            delayDict.setdefault(i, (nodeDict.get(i)).getDelay())
    else:
        for name in nodeList:
            parentDelay = 0.0
            parent = (nodeDict.get(name).getParent())
            parentDelay = delayDict.get(parent)
            delayDict.setdefault(name, ((nodeDict.get(name).getDelay())+parentDelay))
    return delayDict        


# Testing Done
# It returns the actual delay of all the nodes from the network        
def findActualDelay(nodeDict):
    delayDict = {}
    childNodes = findServerAsParent(nodeDict)
    while(nodeDict):
        toRemoveNodes = []
        delayDict = appendToDelayDict(delayDict, childNodes, nodeDict)
        toRemoveNodes = childNodes            
        childNodes = getNextChildNodes(toRemoveNodes, nodeDict)
        nodeDict = removeNodefromDict(toRemoveNodes, nodeDict)
    return delayDict

# Testing done
# It adds all the child node to bandwidthDict(Dictionary) along with its
# actual bandwidth. If its server has less upload bandwidth than itself the bandwidth
# of the child preceeding that server will have the same bandwidth but if it has less
# bandwidth than server its bandwidth will remain the same.
def appendToBandwidthDict(bandwidthDict, nodeList, nodeDict):
    if not bandwidthDict:
        for i in nodeList:
            bandwidthDict.setdefault(i, (nodeDict.get(i)).getUBandWidth())
    else:
        for i in nodeList:
            parent = (nodeDict.get(i).getParent())
            bandwidth = 0.0
            parentBandwidth = bandwidthDict.get(parent)
            if (parentBandwidth < nodeDict.get(i).getUBandWidth()):
                bandwidth = parentBandwidth
                bandwidthDict.setdefault(i, parentBandwidth)
            else:
                bandwidth = nodeDict.get(i).getUBandWidth()
            bandwidthDict.setdefault(i, bandwidth)                
    return bandwidthDict

# Testing done
# It returns the actual delay of all the nodes from the network        
def findActualBandwidth(nodeDict):
    bandwidthDict = {}
    childNode = findServerAsParent(nodeDict)
    while(nodeDict):
        toRemoveNode = []
        bandwidthDict = appendToBandwidthDict(bandwidthDict, childNode, nodeDict)
        toRemoveNode = childNode
        childNode = getNextChildNodes(toRemoveNode, nodeDict)
        nodeDict = removeNodefromDict(toRemoveNode, nodeDict)
    return bandwidthDict

# Testing done
# It normalizes all the values of the network and returns it in normalizedDict(Dictionary)
def normalize(nodeDict):
    normalizedDict = {}
    # Copy the original nodeDict so that there will be changes in the original dictionary
    bDict = copy.copy(nodeDict)
    dDict = copy.copy(nodeDict)
    oDict = copy.copy(nodeDict)
    # Find the actual delay of all the nodes and returns as delayDict(Dictionary)
    # E.g. delayDicy = {'A': 1.0, 'B': 2.0}
    delayDict = findActualDelay(dDict)
    # Find the actual bandwidth of all the nodes and returns as delayDict(Dictionary)
    # E.g. delayDicy = {'A': 33.45, 'B': 31.23}
    bandwidthDict = findActualBandwidth(bDict)
    # Get the actual high and low delay/bandwidth of the overall network
    hiLoMap = findHiLoDB(delayDict, bandwidthDict)
    hiDelay = hiLoMap.get('hiDelay')
    loDelay = hiLoMap.get('loDelay')
    hiBandwidth = hiLoMap.get('hiBandwidth')
    loBandwidth = hiLoMap.get('loBandwidth')
    # Iterating the whole oDict(Dictionary) so as normalize all the nodes
    for key in oDict:
        bandwidth = bandwidthDict.get(key)
        delay = delayDict.get(key)
        bandFactor = ((bandwidth - loBandwidth)/(hiBandwidth - loBandwidth)) * 100
        delayFactor = 100 - (((delay - loDelay)/(hiDelay - loDelay)) * 100)
        normVal = (bandFactor + delayFactor)/2
        normalizedDict.setdefault(key, normVal)
    # A normalizedDict(Dictionary) that returns all the normalized value of the nodes in the network
    # E.g. {'A':32.12, 'B':43.78}
    return normalizedDict

# It gives the best node(object of class Info()) based on the normalized value of the whole network
def getNodeAsServer(nodeDict):
    # A normalized normaizedDict(Dictinary) of the whole network
    normalizedDict = normalize(nodeDict)
    # Sorting the normalized Dictianary so as to get the highest normalized factor
    Nodes = collections.namedtuple('Nodes', 'val name')
    ascNode = sorted([Nodes(a, b) for (b, a) in normalizedDict.items()])
    selectedNode = ascNode[-1]
    # Its an object of Info(), the node with best normalized factor
    nodeObj = nodeDict.get(selectedNode.name)
    return nodeObj
